package com.aps.algorithm.heuristic;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.aps.algorithm.core.AbstractAPSAlgorithm;
import com.aps.algorithm.entity.BasicEquipSchedulingResult;
import com.aps.algorithm.heuristic.genetic.ApsDefaultInitializePopulation;
import com.aps.algorithm.heuristic.genetic.dto.ApsOperationDto;
import com.aps.algorithm.heuristic.genetic.model.AbstractInitializePopulation;
import com.aps.algorithm.heuristic.genetic.model.Chromosome;
import com.aps.algorithm.mapper.BasicEquipSchedulingResultMapper;
import com.aps.algorithm.utils.output.OutputUtils;
import com.aps.common.constant.Constants;
import com.aps.common.entity.ApsDayOfWeek;
import com.aps.common.entity.ApsInterval;
import com.aps.common.model.ApsParameterModel;
import com.aps.common.model.genetic.Equipment;
import com.aps.common.model.genetic.Order;
import com.aps.common.model.genetic.OrderProcess;
import com.aps.common.utils.converter.EquipmentConverter;
import com.aps.common.utils.converter.OrderConverter;
import com.aps.framework.exception.BusinessException;
import com.aps.framework.exception.ErrorCode;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.security.SecureRandom;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 遗传算法 (Genetic Algorithm)
 * 用于解决复杂的APS优化问题
 */
@Slf4j
@Component
public class GeneticAlgorithm extends AbstractAPSAlgorithm {
    private final Logger logger = LoggerFactory.getLogger(GeneticAlgorithm.class);
    private static final String ALGORITHM_NAME = "GeneticAlgorithm";
    private static final String ALGORITHM_DESCRIPTION = "遗传算法 - 通过进化计算寻找最优排程方案";

    private List<Equipment> equipmentList = new ArrayList<>();
    private List<Order> orderList = new ArrayList<>();

    @Override
    public String getAlgorithmName() {
        return ALGORITHM_NAME;
    }

    @Override
    public String getAlgorithmDescription() {
        return ALGORITHM_DESCRIPTION;
    }

    @Resource
    @Lazy
    private StrategyContext strategyContext;

    @Resource
    BasicEquipSchedulingResultMapper basicEquipSchedulingResultMapper;

    @Override
    protected Chromosome<ApsOperationDto> doExecute(ApsParameterModel apsParameterModel) {
        //1、APS标准参数模型转换为遗传算法的参数模型
        List<Order> orders = OrderConverter.convert(apsParameterModel.getApsOrders());
        List<Equipment> equipments = EquipmentConverter.convert(apsParameterModel.getApsEquipments(), (LocalDateTime) this.parameters.getParameter(Constants.GLOBAL_START_TIME));
        equipmentList.addAll(equipments);
        //2、以订单ID为key，对应订单列表为value的Map，以设备类型为key，对应设备列表为value的Map
        Map<String, Order> orderMap = OrderConverter.convertToMap(orders);
        Map<String, List<Equipment>> equipmentMap = EquipmentConverter.convertToMap(equipments);
        //3、创建基因编码
        List<OrderProcess> orderProcesses = CreateGeneEncode(orders);
        //4、初始化种群
        AbstractInitializePopulation<ApsOperationDto>.InitialPopulationData initialPopulationData =
                new ApsDefaultInitializePopulation(orderProcesses, (Long) this.parameters.getParameter(Constants.POPULATION_SIZE), (Long) this.parameters.getParameter(Constants.MAX_PERMUTATION_THRESHOLD)).doInitializePopulation();
        logger.info("初始化种群完成，基因数量：{}", initialPopulationData.getChromosomeList().size());
        logger.info("初始化种群完成，初始化策略：{}", initialPopulationData.getInitialPopulationType());
        logger.info("初始化种群完成，基因序列");
        initialPopulationData.getChromosomeList().forEach(chromosome -> {
            log.info("基因序列：\n{}", JSONUtil.toJsonPrettyStr(chromosome.getGeneSequence()));
        });

        //选择计算适应度方法
        // 根据参数获取合适的策略并执行
        GeneticAlgorithmStrategy strategy = strategyContext.getStrategy("chemical");
        strategy.setParameters(parameters);

        //输出的排产方案集合
        List<Chromosome<ApsOperationDto>> solutionResults = new ArrayList<>();
        solutionResults = initialPopulationData.getChromosomeList();
        //全排列模式，不需要进行迭代、交叉、变异，只需要计算最优解即可
        if (initialPopulationData.getInitialPopulationType().equals(AbstractInitializePopulation.InitialPopulationType.ALL)) {
            solutionResults.forEach(chromosome -> {
                GeneticStrategyParams params = new GeneticStrategyParams.Builder()
                        .addGeneSequence(chromosome.getGeneSequence())
                        .addOrderMap(orderMap)
                        .addEquipmentMap(equipmentMap)
                        .build();
                // 重置设备可用时间、设备已安排的工序时间段列表，用于冲突检测和利用率计算
                resetMachineAvailability(equipmentMap);
                //重置订单工序时间区间列表
                resetOrderProcessIntervalList(params.getGeneSequence());
                //重置orderMap中每个订单的工序
                orderMap.values().forEach(order -> order.setProcessList(new ArrayList<>()));
                chromosome.setFitness(strategy.calSingleChromosome(params));
            });
        } else {//部分排列模式，需要进行迭代、交叉、变异
            for (int generation = 0; generation < (int) this.parameters.getParameter(Constants.MAX_GENERATIONS); generation++) {
                logger.info("*****************************计算染色体适应度开始*****************************");
                //1、计算染色体适应度
                solutionResults.forEach(chromosome -> {
                    GeneticStrategyParams params = new GeneticStrategyParams.Builder()
                            .addGeneSequence(chromosome.getGeneSequence())
                            .addOrderMap(orderMap)
                            .addEquipmentMap(equipmentMap)
                            .build();
                    // 重置设备可用时间、设备已安排的工序时间段列表，用于冲突检测和利用率计算
                    resetMachineAvailability(equipmentMap);
                    //重置订单工序时间区间列表
                    resetOrderProcessIntervalList(params.getGeneSequence());
                    //重置orderMap中每个订单的工序
                    orderMap.values().forEach(order -> order.setProcessList(new ArrayList<>()));
                    chromosome.setFitness(strategy.calSingleChromosome(params));
                });
                logger.info("*****************************计算染色体适应度结束*****************************");
                logger.info("*****************************选择父代开始*****************************");
                //2、选择父代
                List<Chromosome<ApsOperationDto>> parents = strategy.selectParents(solutionResults);
                logger.info("*****************************选择父代结束*****************************");
                logger.info("*****************************交叉开始*****************************");
                //3、交叉
                solutionResults.clear();
                solutionResults = pmxCrossover(parents);
                logger.info("*****************************交叉结束*****************************");
                logger.info("*****************************变异开始*****************************");
                //4、变异
                List<Chromosome<ApsOperationDto>> solutionMutateResults = new ArrayList<>();
                solutionResults.forEach(chromosome -> strategy.mutate(chromosome,solutionMutateResults));
                solutionResults.clear();
                solutionResults.addAll(solutionMutateResults);
                logger.info("*****************************变异结束*****************************");
            }
        }
        logger.info("遗传计算完成");
        solutionResults.sort(Comparator.comparing(Chromosome::getFitness));
        for (Chromosome<ApsOperationDto> solutionResult : solutionResults) {
            System.out.println(solutionResult.getChromosomeCode());
        }

        List<BasicEquipSchedulingResult> equipSchedulingResultList = new ArrayList<>();
        String schedulingId = String.valueOf(this.parameters.getParameter(Constants.CONSTRAINT_ID));
        AtomicInteger index = new AtomicInteger(0);
        //保存排产结果 设备纬度，先保存到redis中，当方案对比之后，选择了那个就把选择的保存到数据库中
        solutionResults.forEach(chromosome -> {
            index.getAndIncrement();
            ((List<ApsOperationDto>) chromosome.getGeneSequence()).forEach(child -> {
                child.getOrderProcess().getProcessIntervalList().forEach(processInterval -> {
                    BasicEquipSchedulingResult equipSchedulingResult = new BasicEquipSchedulingResult();
                    equipSchedulingResult.setTenantId("1");
                    equipSchedulingResult.setPlan(schedulingId);
                    equipSchedulingResult.setPlanIndex("序号:" + index);
                    equipSchedulingResult.setOrderId(child.getOrderProcess().getOrderId());
                    equipSchedulingResult.setEquipId(processInterval.getMachineId());
                    equipSchedulingResult.setProcessId(child.getOrderProcess().getId());
                    equipSchedulingResult.setProcessStartTime(processInterval.getStartTime());
                    equipSchedulingResult.setProcessEndTime(processInterval.getEndTime());
                    equipSchedulingResultList.add(equipSchedulingResult);
                });
            });
        });
//        basicEquipSchedulingResultMapper.insert(equipSchedulingResultList,500);
//        RedisUtils.setObjectIfAbsent("aps:scheduling:result:" + schedulingId, JSONUtil.toJsonPrettyStr(equipSchedulingResultList), Duration.ofMinutes(30));
        if (CollectionUtil.isEmpty(solutionResults)) {
            return null;
        }

        log.info("适应度最低分分数：{}", solutionResults.get(0).getGeneSequence());
        log.info("适应度最高分分数：{}", solutionResults.get(solutionResults.size() - 1).getGeneSequence());

        //返回最优染色体。
        Chromosome<ApsOperationDto> chromosome = solutionResults.get(solutionResults.size() - 1);
        return chromosome;

//        List<BasicEquipSchedulingResult> resultList = new ArrayList<>();
//        List<ApsOperationDto> geneSequence =
//                solutionResults.get(solutionResults.size() - 1).getGeneSequence();
//        for (ApsOperationDto apsOperationDto : geneSequence) {
//            for (ProcessInterval processInterval :
//                    apsOperationDto.getOrderProcess().getProcessIntervalList()) {
//                BasicEquipSchedulingResult equipSchedulingResult = new BasicEquipSchedulingResult();
//                equipSchedulingResult.setTenantId("1");
//                equipSchedulingResult.setPlan(schedulingId);
//                equipSchedulingResult.setPlanIndex("序号:" + index);
//                equipSchedulingResult.setOrderId(apsOperationDto.getOrderProcess().getOrderId());
//                equipSchedulingResult.setEquipId(processInterval.getMachineId());
//                equipSchedulingResult.setProcessId(apsOperationDto.getOrderProcess().getId());
//                equipSchedulingResult.setProcessStartTime(processInterval.getStartTime());
//                equipSchedulingResult.setProcessEndTime(processInterval.getEndTime());
//                resultList.add(equipSchedulingResult);
//            }
//        }

    }

    protected void resetOrderProcessIntervalList(List<ApsOperationDto> geneSequence) {
        geneSequence.forEach(apsOperationDto -> {
            apsOperationDto.getOrderProcess().resetAvailability();
        });
    }

    /**
     * @description: 重置设备可用时间、设备已安排的工序时间段列表，用于冲突检测和利用率计算
     * @author: jsy
     * @time: 2025/10/4 15:12
     */
    protected void resetMachineAvailability(Map<String, List<Equipment>> equipmentMap) {
        equipmentMap.values().forEach(equipmentList -> {
            equipmentList.forEach(equipment -> {
                equipment.resetAvailability((LocalDateTime) this.parameters.getParameter(Constants.GLOBAL_START_TIME));
            });
        });
    }

    /**
     * @description: 选择最早可用且能满足时间要求的设备
     * @author: jsy
     * @time: 2025/9/23 15:27
     */
    protected Equipment selectBestMachine(OrderProcess op, LocalDateTime earliestPossibleStart, Map<String, List<Equipment>> equipmentMap) {
        if (op.getEquipmentType() == null) {
            return null;
        }
        List<Equipment> candidateMachines = equipmentMap.get(op.getEquipmentType());
        if (candidateMachines == null || candidateMachines.isEmpty()) {
            throw new BusinessException(ErrorCode.NO_AVAILABLE_EQUIPMENT);
        }

        // 选择最早可用且能满足时间要求的设备
        return candidateMachines.stream()
                .min(Comparator.comparing(machine -> {
                    LocalDateTime machineAvailable = machine.getNextAvailableTime();
                    return Duration.between(earliestPossibleStart,
                                    machineAvailable.isAfter(earliestPossibleStart) ? machineAvailable : earliestPossibleStart)
                            .toMinutes();
                }))
                .orElse(null);
    }

    /**
     * @description: 计算结束时间
     * LocalDate 是年月日
     * LocalDateTime 是年月日时分秒
     * LocalTime 是时分秒
     * @author: jsy
     * @time: 2025/9/23 15:43
     */
    protected LocalDateTime calculateEndTime(LocalDateTime time, double processingTime) {
        if (processingTime <= 0) {
            return time;
        }

        LocalDateTime currentTime = time;
        double remainingTime = processingTime;
        while (remainingTime > 0) {
            LocalDate currentDate = currentTime.toLocalDate();
            // 获取当天的时段配置（正常时段+加班时段）
            ApsDayOfWeek apsDayOfWeek = getWorkInfoMap(currentTime);
            if (apsDayOfWeek == null) {
                // 当天无任何时段配置，直接跳至下一天0点
                currentTime = currentDate.plusDays(1).atStartOfDay();
                continue;
            }
            // 合并正常时段和加班时段并去空
            List<ApsInterval> intervals = new ArrayList<>();
            if (CollectionUtil.isNotEmpty(apsDayOfWeek.getApsIntervalNormal())) {
                intervals.addAll(apsDayOfWeek.getApsIntervalNormal());
            }
            ApsInterval overInterval = apsDayOfWeek.getApsIntervalOver();
            if (overInterval != null) {
                intervals.add(overInterval);
            }
            // 按开始时间排序所有时段，确保时间顺序处理
            intervals.sort(Comparator.comparing(ApsInterval::getStartTime));
            boolean processedInDay = false;
            for (ApsInterval interval : intervals) {
                //每一段的工作开始时间
                LocalTime startTime = interval.getStartTime();
                //每一段的工作结束时间
                LocalTime endTime = interval.getEndTime();
                //去年年月日
                LocalTime currentTimeOfDay = currentTime.toLocalTime();
                // 确定当前时段内的有效开始时间
                LocalTime periodStart;
                if (currentTimeOfDay.isBefore(startTime)) {
                    // 当前时间在时段开始前，从时段开始时间算起
                    periodStart = startTime;
                    currentTime = LocalDateTime.of(currentDate, periodStart);
                } else if (currentTimeOfDay.isAfter(endTime)) {
                    // 当前时间已过时段结束，跳过该时段
                    continue;
                } else {
                    // 当前时间在时段内，从当前时间算起
                    periodStart = currentTimeOfDay;
                    currentTime = LocalDateTime.of(currentDate, periodStart);
                }
                // 先获取时段内的总秒数，再转换为分钟（保留小数）
                long secondsInPeriod = Duration.between(periodStart, endTime).getSeconds();
                // 直接用秒转分钟，确保小数精度
                double minutesInPeriod = secondsInPeriod / 60.0; // 直接用秒转分钟，确保小数精度
                // 无效时段（开始时间晚于/等于结束时间）
                if (minutesInPeriod <= 0) {
                    continue;
                }
                if (remainingTime <= minutesInPeriod) {
                    // 剩余时间可在当前时段内完成，计算结束时间
                    long wholeMinutes = (long) remainingTime; // 整数分钟部分
                    double fractionalMinutes = remainingTime - wholeMinutes; // 小数分钟部分
                    long fractionalSeconds = (long) (fractionalMinutes * 60); // 转换为秒
                    return currentTime.plusMinutes(wholeMinutes).plusSeconds(fractionalSeconds);
                } else {
                    // 消耗当前时段的全部时间，更新剩余时间和当前时间
                    remainingTime -= minutesInPeriod;
                    currentTime = LocalDateTime.of(currentDate, endTime);
                    processedInDay = true;
                    break; // 跳出当前时段循环，重新检查当天剩余时段
                }
            }
            // 若当天所有时段均处理完毕仍有剩余时间，跳到下一天0点
            if (!processedInDay) {
                currentTime = currentDate.plusDays(1).atStartOfDay();
            }
        }
        // 剩余时间为0时返回当前时间
        return currentTime;
//        while (remainingTime > 0) {
//            if (isRestTime(currentTime)) {
//                currentTime = currentTime.withHour((int) this.parameters.getParameter(Constants.AFTERNOON_WORK_START_HOUR)).withMinute(0);
//                continue;
//            }
//
//            if (isNonWorkingTime(currentTime)) {
//                currentTime = getNextWorkingPeriodStart(currentTime);
//                continue;
//            }
//
//            double timeInCurrentPeriod = getRemainingTimeInPeriod(currentTime);
//            double processInThisPeriod = Math.min(remainingTime, timeInCurrentPeriod);
//
//            currentTime = currentTime.plusMinutes((long) processInThisPeriod);
//            remainingTime -= processInThisPeriod;
//        }

//        return currentTime;
    }

//    /**
//     * @description: 检查是否为休息时间
//     * @author: jsy
//     * @time: 2025/9/13 15:04
//     */
//    private boolean isRestTime(LocalDateTime time) {
//        // 获取当天的工作信息（包含正常时段和加班时段）
//        ApsDayOfWeek apsDayOfWeek = getWorkInfoMap(time);
//        // 若当天无工作信息配置，默认视为休息时间
//        if (apsDayOfWeek == null) {
//            return true;
//        }
//        // 获取当天的正常工作时段集合
//        List<ApsInterval> normalIntervals = apsDayOfWeek.getApsIntervalNormal();
//        // 若正常工作时段为空，视为全天休息
//        if (CollectionUtil.isEmpty(normalIntervals)) {
//            return true;
//        }
//        // 提取当前时间的时分部分（忽略日期）
//        LocalTime targetTime = time.toLocalTime();
//        // 检查是否在任何一个正常工作时段内
//        for (ApsInterval interval : normalIntervals) {
//            LocalTime start = interval.getStartTime();
//            LocalTime end = interval.getEndTime();
//
//            // 若在[startTime, endTime]闭区间内，说明是工作时间，不是休息时间
//            if (!targetTime.isBefore(start) && !targetTime.isAfter(end)) {
//                return false;
//            }
//        }
//        // 不在任何正常工作时段内，视为休息时间
//        return true;
//    }

//    /**
//     * @description: 检查是否为工作时间
//     * @author: jsy
//     * @time: 2025/9/23 15:44
//     */
//    private boolean isNonWorkingTime(LocalDateTime time) {
//        int hour = time.getHour();
//        if (StrUtil.equals(this.parameters.getParameter(Constants.IS_OVERTIME).toString(), "1")) {//是否加班 0 不加班 1 加班
//            return hour < (int) this.parameters.getParameter(Constants.WORK_START_HOUR) || hour >= (int) this.parameters.getParameter(Constants.OVERTIME_END_HOUR) || isRestTime(time);
//        } else {
//            return hour < (int) this.parameters.getParameter(Constants.WORK_START_HOUR) || hour >= (int) this.parameters.getParameter(Constants.WORK_END_HOUR) || isRestTime(time);
//        }
//    }

//    /**
//     * @description: 获取下一个工作时间段的开始时间
//     * @author: jsy
//     * @time: 2025/9/23 15:44
//     */
//    private LocalDateTime getNextWorkingPeriodStart(LocalDateTime time) {
//        int hour = time.getHour();
//        // 获取当天的工作信息（包含正常时段和加班时段）
//        ApsDayOfWeek apsDayOfWeek = getWorkInfoMap(time);
//    }


//    /**
//     * @description: 获取当前时间段的剩余工作时间
//     * @author: jsy
//     * @time: 2025/9/23 15:45
//     */
//    private double getRemainingTimeInPeriod(LocalDateTime time) {
//        int hour = time.getHour();
//        int minute = time.getMinute();
//
//        if (hour < (int) this.parameters.getParameter(Constants.WORK_START_HOUR)) {
//            return 0;
//        } else if (hour < (int) this.parameters.getParameter(Constants.FORENOON_WORK_END_HOUR)) {
//            return ((int) this.parameters.getParameter(Constants.FORENOON_WORK_END_HOUR) - hour) * 60 - minute;
//        } else if (hour == (int) this.parameters.getParameter(Constants.FORENOON_WORK_END_HOUR)) {
//            return 0;
//        } else if (hour < (int) this.parameters.getParameter(Constants.WORK_END_HOUR)) {
//            return ((int) this.parameters.getParameter(Constants.WORK_END_HOUR) - hour) * 60 - minute;
//        } else if (hour < (int) this.parameters.getParameter(Constants.OVERTIME_END_HOUR) && StrUtil.equals(this.parameters.getParameter(Constants.IS_OVERTIME).toString(), "1")) {//Constants.IS_OVERTIME 是否加班 0 不加班 1 加班
//            return ((int) this.parameters.getParameter(Constants.OVERTIME_END_HOUR) - hour) * 60 - minute;
//        } else {
//            return 0;
//        }
//    }


//    /**
//     * @description: 计算设备利用率
//     * @author: jsy
//     * @time: 2025/9/29 14:53
//     */
//    //TODO 设备利用率
//    protected double calculateEquipmentUtilization() {
//        // 设备总加工时间
//        double totalWorkingTime = 0;
//        // 总可用工作时间
//        double totalAvailableTime = 0;
//
//        for (Equipment equipment : equipmentList) {
//            //设备工作时间
//            long workTime = 0;
//            for (ProcessInterval interval : equipment.getEquipmentIntervals()) {
//                workTime += Duration.between(interval.getStartTime(), interval.getEndTime()).toMinutes();
//            }
//
//            // 最晚的交付日期
//            LocalDateTime latestDue = orderList.stream()
//                    .map(Order::getDueDate)
//                    .max(LocalDateTime::compareTo)
//                    .orElse((LocalDateTime) this.parameters.getParameter(Constants.GLOBAL_START_TIME));
//
//            // 一共多少天 = 最晚交付日期 - 全局开始时间 + 1（包括最晚交付日期）
//            long days = ChronoUnit.DAYS.between((LocalDateTime) this.parameters.getParameter(Constants.GLOBAL_START_TIME), latestDue) + 1;
//            // 每天可用时间 = 正常工作时间 + 加班时间
////            int dailyAvailable = ((int) this.parameters.getParameter(Constants.WORK_END_HOUR) - (int) this.parameters.getParameter(Constants.WORK_START_HOUR)) * 60
////                    + ((int) this.parameters.getParameter(Constants.FORENOON_WORK_END_HOUR) - (int) this.parameters.getParameter(Constants.AFTERNOON_WORK_START_HOUR)) * 60 +
////                    ((int) this.parameters.getParameter(Constants.OVERTIME_END_HOUR) - (int) this.parameters.getParameter(Constants.OVERTIME_START_HOUR)) * 60;
//            int dailyAvailable = ((int) this.parameters.getParameter(Constants.FORENOON_WORK_END_HOUR) - (int) this.parameters.getParameter(Constants.WORK_START_HOUR)) * 60
//                    + ((int) this.parameters.getParameter(Constants.WORK_END_HOUR) - (int) this.parameters.getParameter(Constants.AFTERNOON_WORK_START_HOUR)) * 60;
//            if (StrUtil.equals(this.parameters.getParameter(Constants.IS_OVERTIME).toString(), "1")) {//是否加班 0 不加班 1 加班
//                dailyAvailable = dailyAvailable + ((int) this.parameters.getParameter(Constants.OVERTIME_END_HOUR) - (int) this.parameters.getParameter(Constants.OVERTIME_START_HOUR)) * 60;
//            }
//            // 总可用时间 += 一共多少天 * 每天可用时间
//            totalAvailableTime += days * dailyAvailable;
//            // 设备总加工时间 += 设备工作时间
//            totalWorkingTime += workTime;
//        }
//
//        // 设备利用率 = 设备总加工时间 / 总可用工作时间
//        return totalAvailableTime > 0 ? totalWorkingTime / totalAvailableTime : 0;
//    }

    /**
     * @description: 交叉操作（OX交叉）
     * @author: jsy
     * @time: 2025/9/23 16:33
     */
    private List<Chromosome<ApsOperationDto>> pmxCrossover(List<Chromosome<ApsOperationDto>> parents) {
        List<Chromosome<ApsOperationDto>> offspring = new ArrayList<>();

        for (int i = 0; i < (Long) this.parameters.getParameter(Constants.POPULATION_SIZE); i += 2) {
            Chromosome<ApsOperationDto> parent1 = parents.get(i);
            Chromosome<ApsOperationDto> parent2 = parents.get(Math.toIntExact(Math.min(i + 1, (Long) this.parameters.getParameter(Constants.POPULATION_SIZE) - 1)));

            if (Math.random() < (double) this.parameters.getParameter(Constants.CROSSOVER_RATE)) {
                int size = parent1.getGeneSequence().size();
                if (size < 2) {
                    offspring.add(parent1.copy());
                    offspring.add(parent2.copy());
                    continue;
                }

                // MODIFICATION: 改进交叉点选择
                int point1 = new SecureRandom().nextInt(size);
                int point2 = new SecureRandom().nextInt(size);

                if (point1 > point2) {
                    int temp = point1;
                    point1 = point2;
                    point2 = temp;
                }

                Chromosome<ApsOperationDto> child1 = parent1.copy();
                Chromosome<ApsOperationDto> child2 = parent2.copy();

                // 构建映射关系
                Map<String, ApsOperationDto> mapChild1 = new HashMap<>();
                Map<String, ApsOperationDto> mapChild2 = new HashMap<>();
                // 交换基因片段
                List<ApsOperationDto> tempChild1 = new ArrayList<>(child1.getGeneSequence().subList(point1, point2 + 1));
                List<ApsOperationDto> tempChild2 = new ArrayList<>(child2.getGeneSequence().subList(point1, point2 + 1));
                tempChild1.forEach(operationDto -> mapChild1.put(operationDto.getGeneCode(), operationDto));
                tempChild2.forEach(operationDto -> mapChild2.put(operationDto.getGeneCode(), operationDto));

                child1.getGeneSequence().subList(point1, point2 + 1).clear();
                child1.getGeneSequence().addAll(point1, tempChild2);
                child2.getGeneSequence().subList(point1, point2 + 1).clear();
                child2.getGeneSequence().addAll(point1, tempChild1);

                List<List<ApsOperationDto>> parentGenomes = Arrays.asList(parent1.getGeneSequence(), parent2.getGeneSequence());
                // 修复交叉区间外的基因（避免重复）
                removeDuplicates(child1.getGeneSequence(), parentGenomes);
                removeDuplicates(child2.getGeneSequence(), parentGenomes);

                // 修复染色体，确保订单内工序顺序 TODO
                repairOperationSequence(child1.getGeneSequence());
                repairOperationSequence(child2.getGeneSequence());

                validateNoDuplicates(child1.getGeneSequence());
                validateNoDuplicates(child2.getGeneSequence());
                // 重新生成 chromosomeCode（关键！变异后编码会变，必须重新计算）
                child1.preparedChromosomeCode();
                child2.preparedChromosomeCode();
                boolean child1Flag = addIfCodeNotDuplicate(offspring, child1);
                if (!child1Flag) {
                    i--;
                }
                boolean child2Flag = addIfCodeNotDuplicate(offspring, child2);
                if (!child2Flag) {
                    i--;
                }
            } else {
                offspring.add(parent1.copy());
                offspring.add(parent2.copy());
            }
        }

        if (offspring.size() > (Long) this.parameters.getParameter(Constants.POPULATION_SIZE)) {
            offspring = offspring.subList(0, Math.toIntExact((Long) this.parameters.getParameter(Constants.POPULATION_SIZE)));
        }

        return offspring;
    }

    /**
     * @description: 修复重复基因的问题
     * @author: jsy
     * @time: 2025/10/4 18:14
     */
    protected void removeDuplicates(List<ApsOperationDto> child, List<List<ApsOperationDto>> parentGenomes) {
        // 1. 检测基因编码冲突
        Map<String, List<Integer>> conflictMap = detectConflicts(child);
        if (conflictMap.isEmpty()) {
            return; // 无冲突，直接返回
        }

        // 2. 构建候选基因库
        List<ApsOperationDto> candidatePool = buildCandidatePool(child, parentGenomes);

        // 3. 执行冲突消解
        resolveConflicts(child, conflictMap, candidatePool, parentGenomes);

        // 4. 最终校验（确保去重彻底性）
        validateNoDuplicates(child);
    }

    /**
     * @description: 冲突消解执行器：替换子代中的重复基因，维持序列长度不变
     * @author: jsy
     * @time: 2025/10/4 19:05
     */
    private void resolveConflicts(List<ApsOperationDto> child,
                                  Map<String, List<Integer>> conflictMap,
                                  List<ApsOperationDto> candidatePool,
                                  List<List<ApsOperationDto>> parentGenomes) {
        // 记录当前子代已使用的编码（动态更新）
        Set<String> usedCodes = child.stream()
                .map(ApsOperationDto::getGeneCode)
                .collect(Collectors.toSet());

        // 遍历每个冲突编码，替换重复位点
        for (Map.Entry<String, List<Integer>> entry : conflictMap.entrySet()) {
            String conflictCode = entry.getKey();
            List<Integer> conflictPositions = entry.getValue();

            // 保留第一个出现的位点，替换其余冲突位点
            for (int i = 1; i < conflictPositions.size(); i++) {
                int targetPos = conflictPositions.get(i);
                ApsOperationDto replacement = findReplacement(targetPos, candidatePool, usedCodes, parentGenomes);

                // 执行替换并更新状态
                child.set(targetPos, replacement);
                usedCodes.add(replacement.getGeneCode());
            }
        }
    }

    /**
     * @description: 智能替换基因查找：基于位置关联性与基因池优先级的启发式搜索
     * @author: jsy
     * @time: 2025/10/4 19:05
     */
    private ApsOperationDto findReplacement(int targetPos,
                                            List<ApsOperationDto> candidatePool,
                                            Set<String> usedCodes,
                                            List<List<ApsOperationDto>> parentGenomes) {
        // 策略1：优先从候选基因库中获取（最高优先级）
        if (!candidatePool.isEmpty()) {
            return candidatePool.remove(0);
        }

        // 策略2：从父代对应位置查找未使用的基因（位置关联性优先）
        for (List<ApsOperationDto> parent : parentGenomes) {
            if (targetPos < parent.size()) {
                ApsOperationDto parentGene = parent.get(targetPos);
                if (!usedCodes.contains(parentGene.getGeneCode())) {
                    return parentGene.deepCopy();
                }
            }
        }

        // 策略3：遍历父代基因库寻找可用基因（全局搜索兜底）
        for (List<ApsOperationDto> parent : parentGenomes) {
            for (ApsOperationDto gene : parent) {
                if (!usedCodes.contains(gene.getGeneCode())) {
                    return gene.deepCopy();
                }
            }
        }

        // 极端情况：基因池耗尽（理论上不应发生，因基因总数守恒）
        throw new IllegalStateException("基因替换失败：候选基因池已耗尽，无法消解冲突");
    }

    /**
     * @description:构建候选基因库：从父代基因中筛选未在子代中出现的基因，作为冲突替换源 特性：优先保留父代基因的原始分布特征，避免遗传信息丢失
     * @author: jsy
     * @time: 2025/10/4 19:02
     */
    private List<ApsOperationDto> buildCandidatePool(List<ApsOperationDto> child,
                                                     List<List<ApsOperationDto>> parentGenomes) {
        // 提取子代已存在的基因编码集
        Set<String> existingCodes = child.stream()
                .map(ApsOperationDto::getGeneCode)
                .collect(Collectors.toSet());

        // 从父代基因库中收集候选基因（去重且未在子代出现）
        Set<String> candidateCodes = new HashSet<>();
        List<ApsOperationDto> candidatePool = new ArrayList<>();

        for (List<ApsOperationDto> parent : parentGenomes) {
            for (ApsOperationDto parentGene : parent) {
                String code = parentGene.getGeneCode();
                if (!existingCodes.contains(code) && !candidateCodes.contains(code)) {
                    // 深度拷贝确保基因独立性，避免跨个体引用污染
                    ApsOperationDto clonedGene = parentGene.deepCopy();
                    candidatePool.add(clonedGene);
                    candidateCodes.add(code);
                }
            }
        }
        return candidatePool;
    }

    /**
     * @description: 遗传算法专用基因去重实现：基于父代基因池的启发式冲突消解策略
     * 设计理念：在消除编码冲突的同时，最大化保留父代基因的遗传特征，维持种群多样性
     * @author: jsy
     * @time: 2025/10/4 19:01
     */
    private Map<String, List<Integer>> detectConflicts(List<ApsOperationDto> child) {
        Map<String, List<Integer>> conflictMap = new HashMap<>(child.size());
        for (int i = 0; i < child.size(); i++) {
            ApsOperationDto gene = child.get(i);
            String geneCode = gene.getGeneCode();
            conflictMap.computeIfAbsent(geneCode, k -> new ArrayList<>(2))
                    .add(i); // 仅记录出现多次的编码位点
        }
        // 过滤掉无冲突的编码（仅保留出现次数>1的条目）
        return conflictMap.entrySet().stream()
                .filter(entry -> entry.getValue().size() > 1)
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (v1, v2) -> v1, // 合并策略（理论上不会触发）
                        () -> new LinkedHashMap<>(conflictMap.size()) // 保持插入顺序
                ));
    }

    /**
     * @description: 修复工序顺序，确保同一工件的工序顺序正确
     * @author: jsy
     * @time: 2025/10/4 16:03
     */
    protected void repairOperationSequence(List<ApsOperationDto> sequence) {
        if (sequence == null || sequence.isEmpty()) {
            return;
        }

        // 1. 按订单ID分组，记录每个工序在原列表中的索引和对象
        Map<String, List<AbstractMap.SimpleEntry<Integer, ApsOperationDto>>> orderProcessEntries = new HashMap<>();
        for (int i = 0; i < sequence.size(); i++) {
            ApsOperationDto dto = sequence.get(i);
            OrderProcess orderProcess = dto.getOrderProcess();
            if (orderProcess == null) {
                continue; // 跳过无效工序（无订单工序信息）
            }

            String orderId = orderProcess.getOrderId();
            // 存储索引和对应的工序对象
            orderProcessEntries.computeIfAbsent(orderId, k -> new ArrayList<>())
                    .add(new AbstractMap.SimpleEntry<>(i, dto));
        }

        // 2. 检查每个订单的工序顺序，仅修复顺序错误的组
        for (List<AbstractMap.SimpleEntry<Integer, ApsOperationDto>> entries : orderProcessEntries.values()) {
            // 检查当前组的工序是否已按sequence升序排列
            boolean isOrderCorrect = true;
            for (int i = 1; i < entries.size(); i++) {
                int prevSeq = entries.get(i - 1).getValue().getOrderProcess().getSequence();
                int currSeq = entries.get(i).getValue().getOrderProcess().getSequence();
                if (currSeq < prevSeq) {
                    isOrderCorrect = false;
                    break;
                }
            }

            // 如果顺序错误，进行修复
            if (!isOrderCorrect) {
                // 按sequence升序排序当前组的工序
                List<ApsOperationDto> sortedDtos = entries.stream()
                        .map(AbstractMap.SimpleEntry::getValue)
                        .sorted(Comparator.comparingInt(dto -> dto.getOrderProcess().getSequence()))
                        .collect(Collectors.toList());

                // 将排序后的工序放回原列表的对应索引位置（保持原索引位置不变，仅替换内容）
                for (int i = 0; i < entries.size(); i++) {
                    int originalIndex = entries.get(i).getKey();
                    sequence.set(originalIndex, sortedDtos.get(i));
                }
                OutputUtils.output(sequence, "修复后的工序顺序");
            }
        }
    }

    /**
     * @description: 创建基因编码
     * @author: jsy
     * @time: 2025/9/22 14:41
     */
    private List<OrderProcess> CreateGeneEncode(List<Order> orders) {
        return orders.stream()
                .map(Order::getProcessList)
                .flatMap(List::stream)
                .collect(Collectors.toList());
    }

    /**
     * @description: 验证基因编码中是否存在重复
     * @author: jsy
     * @time: 2025/10/4 18:31
     */
    protected void validateNoDuplicates(List<ApsOperationDto> sequence) {
        Set<String> codes = new HashSet<>();
        for (ApsOperationDto dto : sequence) {
            String code = dto.getGeneCode();
            if (codes.contains(code)) {
                OutputUtils.output(sequence, "存在重复基因的染色体");
                throw new IllegalStateException("存在重复基因编码: " + code);
            }
            codes.add(code);
        }
    }

    /**
     * @description: 根据年月日获取今天是周几，返回对应的工作时间、加班时间、休息时间
     * @author: jsy
     * @time: 2025/10/17 16:42
     */
    protected ApsDayOfWeek getWorkInfoMap(LocalDateTime date) {
        if (date == null) {
            return null;
        }
        int value = date.getDayOfWeek().getValue();
        Map<Integer, ApsDayOfWeek> workInfo = (Map<Integer, ApsDayOfWeek>) this.parameters.getParameter(Constants.WORK_INFO);
        ApsDayOfWeek apsDayOfWeek = workInfo.get(value);
//        if (apsDayOfWeek == null) {
//            throw new IllegalArgumentException("未配置第" + value + "天的工作信息");
//        }
        return apsDayOfWeek;
    }


    //阈值（Threshold）的核心作用是：** 定义 “正常生产场景下，绝大多数情况不会超过的数值” **，以此为界，
    // 对 “常规范围内的波动” 施加敏感惩罚 / 加分，对 “超出范围的极端值” 施加收敛性惩罚 / 加分（增速放缓）。
    //例如：
    //常规加班阈值设为 10 小时：表示 “95% 的正常排程中，总加班时间不会超过 10 小时”，10 小时内的加班是
    // “可接受的常规波动”，惩罚随时间快速增长；超过 10 小时的加班属于 “极端异常”，惩罚增速放缓（避免过度惩罚）。
    //如何合理设定阈值？（3 个核心依据）
    //1. 历史生产数据（最优先）
    //2. 业务规则与约束
    //3. 资源与产能约束

    //S 型曲线实现在制造业 APS 的多目标排程场景中是合理的，核心原因是它完美匹配了 “常规波动需敏感、极端异常需收敛”
    // 的业务需求，同时能严格控制惩罚权重，避免与其他目标（如提前加分、设备负荷）冲突
    //一、S 型曲线惩罚的合理性核心依据
    //S 型曲线的 “前期快速增长、后期缓慢收敛” 特性，恰好贴合制造业排程中对 “常规偏差” 和 “极端偏差” 的不同管理需求，这是线性惩罚（如α×ΣTi）无法替代的。
    //1. 对 “常规偏差” 敏感，符合精细化管理需求
    //制造业中，绝大多数排程偏差（如延期、加班）属于 “可控的常规波动”（如延期 2 天、加班 5 小时），需要通过严格惩罚引导算法优先优化这类问题。S 型曲线的 “前期快速增长” 特性，能让常规偏差的惩罚力度足够大：
    //例：常规延期阈值 = 10 天，最大惩罚权重 = 0.6。当总延期 = 3 天时（常规范围），惩罚 = 0.3126（占满分 31.26%），远高于线性惩罚（若线性 α=0.06，3 天仅惩罚 0.18）。
    //效果：算法会更倾向于选择 “延期 3 天” 而非 “延期 5 天”（5 天惩罚 = 0.4587，比 3 天多 46%），符合 “优先解决小偏差，避免偏差扩大” 的管理逻辑。
    //2. 对 “极端偏差” 收敛，避免击穿满分或扭曲优化方向
    //制造业中偶尔会出现 “不可控的极端偏差”（如设备突发故障导致延期 100 天、紧急插单导致加班 100 小时），若用线性惩罚，会导致惩罚远超满分（如线性 α=0.06，100 天延期惩罚 = 6，远超满分 1），使适应度计算失效。S 型曲线的 “后期收敛” 特性，能让极端偏差的惩罚始终控制在预设权重内：
    //例：100 天延期的惩罚 = 0.5950（接近最大权重 0.6），仅比 10 天延期（0.5243）多 13.5%，既体现了对极端偏差的惩罚，又不会让单一项惩罚击穿满分，确保其他目标（如成本、负荷）仍能影响适应度排名。
    //3. 权重总和可控，适配 “满分 1” 的多目标场景
    //用户的核心场景是 “满分 1”，需要多个惩罚项（延期、加班、负荷等）权重总和≤1。S 型曲线通过 “最大权重” 参数（如延期 0.6、加班 0.2、负荷 0.2），能严格控制每项惩罚的上限，避免多目标叠加后总惩罚超 1。
    //对比线性惩罚：若线性惩罚无上限，极端情况下 “延期惩罚 0.8 + 加班惩罚 0.5=1.3”，会导致适应度为负（1-1.3=-0.3），失去优化意义；而 S 型曲线的两项惩罚最大总和 = 0.6+0.2=0.8，预留 0.2 给其他目标，适配多目标平衡需求。
    //二、S 型曲线惩罚的适用边界（并非所有场景都合理）
    //S 型曲线的合理性依赖 “阈值设定合理” 和 “存在常规 / 极端偏差区分”，以下场景可能更适合其他模型：
    //1. 若 “偏差无常规 / 极端之分”，线性惩罚更简单
    //场景：排程偏差范围极小（如所有订单延期都在 0~3 天内，无极端值），或业务要求 “每多 1 天延期，惩罚力度完全一致”（无敏感度差异）。
    //原因：S 型曲线的 “收敛特性” 在此场景下无意义，线性惩罚（如 α=0.2，3 天延期惩罚 = 0.6）更简单，且计算效率更高。
    //2. 若 “极端偏差必须严格禁止”，阶梯惩罚更合适
    //场景：业务规定 “延期超过 5 天即违约，必须淘汰该排程方案”（极端偏差无容忍度）。
    //原因：S 型曲线对极端偏差仍有收敛惩罚（如 5 天延期惩罚 = 0.5，10 天 = 0.59），但无法直接淘汰方案；此时更适合 “阶梯惩罚”（≤5 天用 S 型，>5 天惩罚 = 1，直接让适应度为 0）。
    //三、总结：S 型曲线惩罚的合理性结论
    //在用户当前的 “制造业 APS、满分 1、多目标排程、偏差可能无上限” 场景下，S 型曲线惩罚是合理且优选的方案，核心支撑点：
    //贴合 “常规偏差严格管、极端偏差不过度罚” 的制造业管理逻辑；
    //严格控制单目标惩罚上限，适配多目标权重总和≤1 的需求；
    //只要阈值基于历史数据或业务规则合理设定（而非随便写），就能准确反映实际优化方向。
    //唯一需要注意的是：阈值设定是 S 型曲线的 “生命线”，若阈值不合理（如把常规偏差误判为极端），再合理的曲线也会失效 —— 这也是之前强调 “阈值不能随便写” 的核心原因。
    //核心设计逻辑
    //以 “延期惩罚” 为例，分三段处理：
    //常规段（≤常规阈值 T1）：用线性惩罚（每单位偏差惩罚均匀，敏感且简单）；
    //极端段（T1 < 偏差 ≤ 硬阈值 T2）：用 S 型曲线惩罚（惩罚增速放缓，避免过度惩罚）；
    //违约段（> 硬阈值 T2）：用阶梯惩罚（直接施加最大惩罚，视为不可接受方案）。
    //其他维度（提前加分、加班惩罚）采用相同逻辑，仅参数（阈值、权重）不同。
    //二、单一模型的选择：线性 vs S 型曲线
    //基于上述维度，单一模型的适用场景如下：
    //1. 线性惩罚（含带硬上限的线性）
    //适用场景：
    //偏差范围小且有明确上限（如 0~5 天延期），无极端值；
    //业务对每单位偏差的敏感度均匀（如 “每延期 1 天，违约金固定”）；
    //需模型简单、易解释（业务人员能直观理解 “延期 2 天的惩罚是 1 天的 2 倍”）。
    //典型案例：
    //电商小件订单（承诺 3 天内送达，延期通常 0~2 天，每延期 1 天赔偿固定金额）；
    //日常生产的加班（每天最多加班 4 小时，无极端加班需求）。
    //注意：若偏差范围大但需用线性，必须加硬上限（如惩罚=min(α×偏差, 最大权重)），避免极端偏差击穿满分。
    //2. S 型曲线惩罚
    //适用场景：
    //偏差范围大且存在极端值（如 0~100 天延期，偶尔出现 50 天以上）；
    //业务对偏差的敏感度 “非均匀”（常规偏差需严格控制，极端偏差可适度容忍，避免过度惩罚）；
    //需平衡多目标权重（如延期、加班、成本的总惩罚需≤1）。
    //典型案例：
    //大型设备制造（生产周期长，可能因供应链问题延期 1~60 天，常规 10 天内需严格优化，超 10 天惩罚增速放缓）；
    //旺季紧急插单（加班可能 0~200 小时，常规 50 小时内需控制，超 50 小时惩罚收敛）。
    //alpha的由来： 最大权重 × 常规段占比（通常取 70%~80%）/ 常规阈值（如10天）；


    /**
     * @param totalDelayDays 总延期天数
     * @param maxWeight      最大惩罚权重（如0.5）
     * @param t1             单位是分钟    常规阈值（如10天-14400分钟，≤t1用线性）
     * @param t2             单位是分钟    硬阈值（如30天-43200分钟，>t2用阶梯惩罚）
     * @param alpha          线性系数（常规段惩罚系数）
     * @return 延期惩罚值（≤maxWeight）
     * @description: 延期惩罚（组合模型：线性+S型+阶梯）
     */
    public double calculateDelayPenalty(double totalDelayDays, double maxWeight, double t1, double t2, double alpha) {
        // 参数合法性校验
        if (maxWeight <= 0 || t1 < 0 || t2 <= t1 || alpha <= 0) {
            throw new IllegalArgumentException("参数不合法：maxWeight>0，t1≥0，t2>t1，alpha>0");
        }
        if (totalDelayDays <= 0) {
            return 0.0;
        }

        // 1. 违约段（>硬阈值t2）：阶梯惩罚（直接最大权重）
        if (totalDelayDays > t2) {
            return maxWeight;
        }

        // 2. 常规段（≤t1）：线性惩罚
        if (totalDelayDays <= t1) {
            double penalty = alpha * totalDelayDays;
            return Math.min(penalty, maxWeight);
        }

        // 3. 极端段（t1 < 偏差 ≤ t2）：S型曲线惩罚
        double linearPenaltyAtT1 = alpha * t1; // t1处的线性惩罚值
        double remainingWeight = maxWeight - linearPenaltyAtT1; // 剩余可分配权重
        if (remainingWeight <= 0) { // 避免线性惩罚已超过最大权重
            return maxWeight;
        }

        double excessDays = totalDelayDays - t1; // 超出t1的天数
        double maxExcessDays = t2 - t1; // 极端段最大偏差


        double k = 1.0 / maxExcessDays; // S型曲线调节系数
        double denominator = Math.log(1 + k * excessDays);
        double numerator = Math.log(1 + k);

        // 极端段惩罚 = 线性基础惩罚 + S型增量惩罚
        double sCurvePenalty = remainingWeight * (1 - numerator / denominator);
        return Math.min(linearPenaltyAtT1 + sCurvePenalty, maxWeight);
    }


    /**
     * @param totalEarlyDays 总提前天数
     * @param maxWeight      最大加分权重（如0.2）
     * @param t1             单位是分钟    常规阈值（如5天-7200分钟，≤t1用线性）
     * @param t2             单位是分钟    硬阈值（如15天-21600分钟，>t2停止加分）
     * @param alpha          线性系数（常规段加分系数）
     * @return 提前加分值（≤maxWeight）
     * @description: 提前完成加分（组合模型：线性+S型+阶梯）
     */
    public double calculateEarlyBonus(double totalEarlyDays, double maxWeight, double t1, double t2, double alpha) {
        // 参数合法性校验
        if (maxWeight <= 0 || t1 < 0 || t2 <= t1 || alpha <= 0) {
            throw new IllegalArgumentException("参数不合法：maxWeight>0，t1≥0，t2>t1，alpha>0");
        }
        if (totalEarlyDays <= 0) {
            return 0.0;
        }

        // 1. 过度提前段（>硬阈值t2）：阶梯加分（保持最大权重）
        if (totalEarlyDays > t2) {
            return maxWeight;
        }

        // 2. 常规段（≤t1）：线性加分
        if (totalEarlyDays <= t1) {
            double bonus = alpha * totalEarlyDays;
            return Math.min(bonus, maxWeight);
        }

        // 3. 极端段（t1 < 偏差 ≤ t2）：S型曲线加分
        double linearBonusAtT1 = alpha * t1; // t1处的线性加分值
        double remainingBonus = maxWeight - linearBonusAtT1; // 剩余可分配加分
        if (remainingBonus <= 0) { // 避免线性加分已超过最大权重
            return maxWeight;
        }

        double excessDays = totalEarlyDays - t1; // 超出t1的天数
        double maxExcessDays = t2 - t1; // 极端段最大偏差

        double k = 1.0 / maxExcessDays; // S型曲线调节系数
        double denominator = Math.log(1 + k * excessDays);
        double numerator = Math.log(1 + k);

        // 极端段加分 = 线性基础加分 + S型增量加分
        double sCurveBonus = remainingBonus * (1 - numerator / denominator);
        return Math.min(linearBonusAtT1 + sCurveBonus, maxWeight);
    }

    /**
     * @param totalOvertimeHours 总加班小时数
     * @param maxWeight          最大惩罚权重（如0.3）
     * @param t1                 单位分钟          常规阈值（如20小时-1200分钟，≤t1用线性）
     * @param t2                 单位分钟          硬阈值（如50小时-3000分钟，>t2用阶梯惩罚）
     * @param alpha              线性系数（常规段惩罚系数）
     * @return 加班惩罚值（≤maxWeight）
     * @description: 加班惩罚（组合模型：线性+S型+阶梯）
     * @author: jsy
     * @time: 2025/10/29 17:20
     */
    public double calculateOvertimePenalty(double totalOvertimeHours, double maxWeight, double t1, double t2, double alpha) {
        // 参数合法性校验
        if (maxWeight <= 0 || t1 < 0 || t2 <= t1 || alpha <= 0) {
            throw new IllegalArgumentException("参数不合法：maxWeight>0，t1≥0，t2>t1，alpha>0");
        }
        if (totalOvertimeHours <= 0) {
            return 0.0;
        }

        // 1. 严重违规段（>硬阈值t2）：阶梯惩罚（直接最大权重）
        if (totalOvertimeHours > t2) {
            return maxWeight;
        }

        // 2. 常规段（≤t1）：线性惩罚
        if (totalOvertimeHours <= t1) {
            double penalty = alpha * totalOvertimeHours;
            return Math.min(penalty, maxWeight);
        }

        // 3. 极端段（t1 < 偏差 ≤ t2）：S型曲线惩罚
        double linearPenaltyAtT1 = alpha * t1; // t1处的线性惩罚值
        double remainingWeight = maxWeight - linearPenaltyAtT1; // 剩余可分配权重
        if (remainingWeight <= 0) { // 避免线性惩罚已超过最大权重
            return maxWeight;
        }

        double excessHours = totalOvertimeHours - t1; // 超出t1的小时数
        double maxExcessHours = t2 - t1; // 极端段最大偏差

        double k = 1.0 / maxExcessHours; // S型曲线调节系数
        double denominator = Math.log(1 + k * excessHours);
        double numerator = Math.log(1 + k);

        // 极端段惩罚 = 线性基础惩罚 + S型增量惩罚
        double sCurvePenalty = remainingWeight * (1 - numerator / denominator);
        return Math.min(linearPenaltyAtT1 + sCurvePenalty, maxWeight);
    }


    /**
     * @param chromosomeList 目标集合
     * @param newChromosome  待添加的染色体
     * @return 是否添加成功（true：成功，false：已存在或参数无效）
     * @description: 向集合添加染色体，仅当chromosomeCode不存在时添加
     * @author: jsy
     * @time: 2025/11/6 17:56
     */
    public static boolean addIfCodeNotDuplicate(List<Chromosome<ApsOperationDto>> chromosomeList,
                                                Chromosome<ApsOperationDto> newChromosome) {
        // 防御性检查
        if (chromosomeList == null || newChromosome == null) {
            return false;
        }

        // 获取并清洗新染色体的code（去前后空格、过滤不可见字符）
        String newCode = cleanCode(newChromosome.getChromosomeCode());
        if (newCode == null) {
            return false; // 清洗后为空视为无效
        }

        // 检查集合中是否存在清洗后相同的code
        boolean codeExists = chromosomeList.stream()
                .map(chrom -> cleanCode(chrom.getChromosomeCode())) // 清洗已有code
                .anyMatch(existingCode -> StrUtil.equals(existingCode, newCode)); // 比较

        // 不重复则添加
        if (!codeExists) {
            return chromosomeList.add(newChromosome);
        }
        return false;
    }
    /**
     * @description: 清洗字符串：去除前后空格、过滤不可见字符
     * @author: jsy
     * @time: 2025/11/6 18:10
     */
    private static String cleanCode(String code) {
        if (code == null) {
            return null;
        }
        //去除前后空格
        return code.trim();
    }
}
